package com.ctosb.classviewer;

import com.ctosb.classviewer.clazz.ClassFile;
import com.ctosb.classviewer.clazz.Field_info;
import com.ctosb.classviewer.clazz.Method_info;
import com.ctosb.classviewer.clazz.cp.Cp_info;
import com.ctosb.classviewer.clazz.util.Cp_infoParse;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;

public class ClassReader {

    private byte[] bytes;
    private int current;

    public ClassReader(String fileName) throws IOException {
        bytes = FileUtils.readFileToByteArray(new File(fileName));
    }

    public Method_info[] readMethods(short length, ClassFile classFile) {
        Method_info[] method_infos = new Method_info[length & 0xffff];
        for (int i = 0; i < method_infos.length; i++) {
            method_infos[i] = new Method_info().parse(this, classFile);
        }
        return method_infos;
    }

    public Field_info[] readFields(short length, ClassFile classFile) {
        Field_info[] field_infos = new Field_info[length & 0xffff];
        for (int i = 0; i < field_infos.length; i++) {
            field_infos[i] = new Field_info().parse(this, classFile);
        }
        return field_infos;
    }

    public short[] readInterfaces(short length) {
        short[] interfaces = new short[length & 0xffff];
        for (int i = 0; i < interfaces.length; i++) {
            interfaces[i] = readShort();
        }
        return interfaces;
    }

    public Cp_info[] readCpInfos(short length, ClassFile classFile) {
        Cp_info[] cp_infos = new Cp_info[length & 0xffff];
        for (int i = 1; i < cp_infos.length; i++) {
            cp_infos[i] = readCpInfo(classFile);
        }
        return cp_infos;
    }

    public Cp_info readCpInfo(ClassFile classFile) {
        try {
            return Cp_infoParse.newCpInfo(this, classFile);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("parse constant pool exception.", e);
        }
    }

    public int readInt() {
        return ((bytes[current++] & 0xff) << 24)
                | ((bytes[current++] & 0xff) << 16)
                | ((bytes[current++] & 0xff) << 8)
                | (bytes[current++] & 0xff);
    }

    public short readShort() {
        return (short) (((bytes[current++] & 0xff) << 8)
                | (bytes[current++] & 0xff));
    }

    public byte[] readBytes(int length) {
        byte[] newbytes = Arrays.copyOfRange(this.bytes, current, current + length);
        current += length;
        return newbytes;
    }

    public byte readByte() {
        return bytes[current++];
    }
}
